package com.easybuilder.common.utils;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.easybuilder.core.authorization.entity.SysGiteeUser;
import com.easybuilder.core.rbac.entity.SysUser;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Component
public class JwtUtil {
    /** 加密秘钥 */
    private static final String SECRET = "jwtwhatsyourname";
    /** token过期时间  2h */
    private static final int EXPIRE_DATE = 2 * 60 * 60 * 1000;

    /** 构建token
     * @param username 用户名
     * @param password 密码
     * @param type 登陆类型
     * */
    public static String buildToken(String username, String password, String type) {
        String token = "";
        try {
            //过期时间
            Date date = new Date(System.currentTimeMillis()+EXPIRE_DATE);
            //秘钥及加密算法
            Algorithm algorithm = Algorithm.HMAC256(SECRET);
            //设置头部信息
            Map<String,Object> header = new HashMap<>();
            header.put("typ","JWT");
            header.put("alg","HS256");
            //携带username，password信息，生成签名
            token = JWT.create()
                    .withHeader(header)
                    .withClaim("username",username)
                    .withClaim("password",password)
                    .withClaim("type", type)
                    .withExpiresAt(date)
                    .sign(algorithm);
        }catch (Exception e){
            e.printStackTrace();
            return  null;
        }
        return token;
    }

    /** 校验token
     *  token: token
     *  requestURI: 请求路径
     * */
    public static boolean checkToken(String token, String requestURI) {
        // 解析token
        Map<String, Claim> claims = resolveToken(token);
        String type = claims.get("type").asString();
        try {
            String username = claims.get("username").as(String.class);
            String password = null;
            if ("SYS".equals(type)) {
                password = claims.get("password").as(String.class);
            }
            // 过期时间，存储时为毫秒，但是获取后单位是秒
            Long exp = claims.get("exp").as(Long.class);
            // 当前时间，单位与过期时间保持一致
            long now = System.currentTimeMillis() / 1000;
            // 如果是退出登录操作，则校验成功
            if (requestURI.equals("/login/logout")) {
                return true;
            } else { // 如果不是退出登录操作
                if ((now > exp && (now - exp) / 60 / 60 >= 1)) {
                    return false;
                } else if (now > exp && (now - exp) / 60 / 60 < 1){
                    // 如果用户携带token已经过期，但是过期未超过一个小时，生成新的token，并且给redis中保存的用户信息续命
                    String newToken = buildToken(username, password, type);
                    // 设置认证信息到响应头中
                    ServletUtil.setToken(newToken);
                    // 保存用户信息到redis（redis保存用户信息3小时，token设置有效时间2小时，如果失效时间 < 1小时，达到自动续命的效果）
                    if ("SYS".equals(type)) {
                        SysUser user = RedisUtil.getValue("USER_"+type+"_" + username, SysUser.class);
                        RedisUtil.setValue("USER_"+type+"_" +username, user, 3L, TimeUnit.HOURS);
                    } else if ("GITEE".equals(type)) {
                        SysGiteeUser user = RedisUtil.getValue("USER_"+type+"_" + username, SysGiteeUser.class);
                        RedisUtil.setValue("USER_"+type+"_" +username, user, 3L, TimeUnit.HOURS);
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("token解析异常，存在伪造token嫌疑!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
            return false;
        }
        // 认证信息通过后，把当前用户信息存储到公用的UserUtil本地线程存储
        String username = claims.get("username").as(String.class);
        if ("SYS".equals(type)) {
            SysUser sysUser = RedisUtil.getValue("USER_"+type+"_" + username, SysUser.class);
            UserUtil.setUser(sysUser);
        }
        return true;
    }

    /** 解析token */
    public static Map<String, Claim> resolveToken(String token){
        Algorithm algorithm = Algorithm.HMAC256(SECRET);
        JWTVerifier jwtVerifier = JWT.require(algorithm).build();
        Map<String, Claim> claims = null;
        DecodedJWT decode = JWT.decode(token);
        //DecodedJWT verify = jwtVerifier.verify(token);

        claims = decode.getClaims();
        return claims;
    }

}
